package com.yzy.fly.config;

import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yzy.fly.FlyEnvContext;
import com.yzy.fly.FlyEnvMap;
import com.yzy.fly.api.annotation.ApiListenerHandler;
import com.yzy.fly.api.function.listener.ApiListenerContext;
import com.yzy.fly.api.function.listener.DefaultApiListenerAdvice;
import com.yzy.fly.api.function.listener.InvokedHandler;
import com.yzy.fly.api.function.listener.InvokedQueue;
import com.yzy.fly.api.function.listener.storage.StorageHandler;
import com.yzy.fly.boot.FlyComponentNames;
import com.yzy.fly.boot.banner.Banner;
import com.yzy.fly.boot.banner.BannerFactory;
import com.yzy.fly.api.config.properties.ApiListenerConfiguration;
import com.yzy.fly.boot.banner.FlyBanner;
import com.yzy.fly.boot.banner.FlyBannerMapper;
import com.yzy.fly.config.properties.BannerConfiguration;
import com.yzy.fly.config.properties.FlyProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

//@ComponentScan(value = {"com.yzy.fly.**"},exclude = {FlyApplication.class})
@Slf4j
//@ConditionalOnMissingBean(FlyApplication.class)
@ComponentScan(value = {"com.yzy.fly.**"},
        excludeFilters = {
//                @ComponentScan.Filter(
//                        type = FilterType.ASSIGNABLE_TYPE,
//                        classes = {ApiVersionRequestMappingHandlerMapping.class}
//                ),
//                @ComponentScan.Filter(
//                        type = FilterType.ASSIGNABLE_TYPE,
//                        classes = {FlyDynamicDataSourceAutoConfiguration.class}
//                ),
                @ComponentScan.Filter(type = FilterType.REGEX, pattern = {
                        "com.yzy.fly.FlyApplication"})
        }
)
//@MapperScan({"com.yzy.fly.mapper.**","com.yzy.fly.api.*.mapper.**","com.yzy.fly.api.**.mapper","com.yzy.fly.api.***.mapper","com.yzy.fly.ide.mapper.**", "com.yzy.fly.rbac.mapper.**","com.yzy.fly.funtion.httpClient.mapper"})
//@MapperScan(value = {"com.**"},annotationClass = Mapper.class)
//@MapperScan({"com.yzy.fly.mapper.**","com.yzy.fly.api.*.mapper","com.yzy.fly.api.**.mapper","com.yzy.fly.api.***.mapper","com.yzy.fly.ide.mapper.**","com.yzy.fly.rbac.mapper.**"})
//@MapperScan(basePackages = {},annotationClass = Mapper.class)
@MapperScan(basePackages = {"com.yzy.fly.*"},annotationClass = Mapper.class)
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({FlyProperties.class})
public class FlyConfig implements InitializingBean {

    private final FlyProperties flyProperties;

    private final ApplicationContext applicationContext;

    public FlyConfig(FlyProperties flyProperties, ApplicationContext applicationContext) {
        this.flyProperties = flyProperties;
        this.applicationContext = applicationContext;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        apiListenerHandlers();
        printBanner();
    }

    public void apiListenerHandlers() throws Exception{
        ApiListenerConfiguration config = flyProperties.getConfiguration().getApi().getListener();
        if (!config.isEnable()) {
            return;
        }
        String[] storageHandlerNames = applicationContext.getBeanNamesForType(StorageHandler.class);
        for (String name : storageHandlerNames) {
            StorageHandler bean = (StorageHandler) applicationContext.getBean(name);
            if (null != bean) {
                Component annotation = bean.getClass().getAnnotation(Component.class);
                if (annotation == null) {
                    int i = 0;
                }
                if (annotation != null &&config.getStorage().equals(annotation.value())) {
                    ApiListenerContext.setSaver(bean);
                    break;
                }
            }
        }
        if (null == ApiListenerContext.getSaver()) {
            throw new Exception("error `fly.configuration.api.listener.storage=" + config.getStorage() + "`, and you can only choose an option in {memory,database,redis} for `fly.configuration.api.listener.storage=`!");
        }

        String[] invokedHandlerNames = applicationContext.getBeanNamesForType(InvokedHandler.class);
        for (String name : invokedHandlerNames) {
//            if ("emailHandler".equals(name) && !mailEnable) {
//                continue;
//            }
            InvokedHandler bean = (InvokedHandler) applicationContext.getBean(name);
//            InvokedHandler bean = applicationContext.getBean(FlyTestKoInvokedHandler.class);
            if (null != bean) {
//                if (FlyTestKoInvokedHandler.class != bean.getClass()) {
//                    continue;
//                }
                ApiListenerHandler annotation = bean.getClass().getAnnotation(ApiListenerHandler.class);
                if (null == annotation) {
                    continue;
                }
                log.info("ApiListenerHandler=>loading class:" + bean.getClass().getName());
                ApiListenerContext.addInvokedHandler(bean);
            }
        }
//        Context.getConfig().setThreadNum(2);
        for (int i = 0; i < 1; i++) {
            new Thread(() -> InvokedQueue.onInveked()).start();
        }
    }

    /**
     * 打印banner
     */
    public void printBanner() {
        BannerConfiguration bannerConfig = flyProperties.getConfiguration().getBanner();
        if (bannerConfig.isEnable()) {
//            Banner banner = BannerFactory.getBanner();
//            banner.banner();
            String implementationVersion = getClass().getPackage().getImplementationVersion();
            FlyBannerMapper bannerMapper = FlySpringContextUtil.getBean(FlyBannerMapper.class);
            QueryWrapper<FlyBanner> queryWrapper = Wrappers.query();
            queryWrapper.eq(FlyBanner.Fields.component, FlyComponentNames.FLY_BOOT);
            FlyBanner banner = bannerMapper.selectOne(queryWrapper);
            System.out.println(banner.getContent());
            System.out.println(" :: Spring Fly Boot ::   (v" + implementationVersion + ")");
        }
    }

//    public static void main(String[] args) {
//        new FlyConfig(null,null).printBanner();
//    }

    @ConditionalOnProperty(prefix = ApiListenerConfiguration.PREFIX, name = "enable", havingValue = "true", matchIfMissing = false)
    @Bean
    public AspectJExpressionPointcutAdvisor configurabledvisor() {
        ApiListenerConfiguration apiListenerConfiguration = flyProperties.getConfiguration().getApi().getListener();
        String pointcut = apiListenerConfiguration.getPointcut();
        if (StringUtils.isBlank(pointcut)) {
            String[] apiListenerPaths = FlyEnvContext.get("apiListenerPaths");
            if (ArrayUtils.isEmpty(apiListenerPaths)) {
                String[] scannerPaths = FlyEnvContext.get("scannerPaths");
                pointcut = scannerPaths[0];
            } else {
                pointcut = apiListenerPaths[0];
            }
        }
        AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
//        String cutRange = "execution(public * com.yzy..*.*(..))";
        String cutRange = "execution(public * " + pointcut + "..*.*(..))";
        cutRange = cutRange + " && !@annotation(com.yzy.fly.api.annotation.ApiListenerHandler)" + " && !@annotation(com.yzy.fly.api.annotation.ApiListenerIgnore) "+ " && !@within(com.yzy.fly.api.annotation.ApiListenerIgnore)";
        advisor.setExpression(cutRange);
        advisor.setAdvice(new DefaultApiListenerAdvice());
        return advisor;
    }

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

//    @Bean
//    public RoleRedisTemplateBeanDefinitionRegistry roleRedisTemplateBeanDefinitionRegistry(){
//        return new RoleRedisTemplateBeanDefinitionRegistry();
//    }
//
//    @Bean
//    public FlyHttpClientBeanDefinitionRegistry flyHttpClientBeanDefinitionRegistry(){
//        return new FlyHttpClientBeanDefinitionRegistry();
//    }

}

